package nachos.vm;

import java.util.*;

import nachos.machine.*;
import nachos.threads.*;
import nachos.userprog.*;
import nachos.vm.*;

/**
 * A kernel that can support multiple demand-paging user processes.
 */
public class VMKernel extends UserKernel {
    /**
     * Allocate a new VM kernel.
     */
    public VMKernel() {
	super();
    }

    /**
     * Initialize this kernel.
     */
    public void initialize(String[] args) {
	super.initialize(args);
	invertPageTable = new InvertTranslationEntry[numPages];
	swapFile = fileSystem.open("Yi_Jin.Swap", true);
	freeSwapPages = new LinkedList<Integer>();
	for (int i = 0;i< numPages;i++)
	{
		freeSwapPages.add(i);
		invertPageTable[i] = new InvertTranslationEntry(i,-1,null,false/*,false*/);
	}
//	lockIpt = new Lock();
	cond = new Condition(lock);
//	semaphoreSwapFile = new Semaphore(1);
    }

    /**
     * Test this kernel.
     */	
    public void selfTest() {
	super.selfTest();
    }

    /**
     * Start running user programs.
     */
    public void run() {
	super.run();
    }
    
    /**
     * Terminate this kernel. Never returns.
     */
    public void terminate() {
/*jill*/fileSystem.remove("Yi_Jin.Swap");
	super.terminate();
    }
    /*jill add*/
    public static InvertTranslationEntry nextPage()
    {
//    	lock.acquire();
       	Processor processor = Machine.processor();
    	TranslationEntry e;
    	int tlbSize = processor.getTLBSize();
		while(numPinned == numPages)
		{
			cond.sleep();
		}
		boolean used = invertPageTable[clockAt].process.getUsed(invertPageTable[clockAt].vpn);
		boolean pinned = invertPageTable[clockAt].pinned;
		while(true)
		{
			if(!pinned && !used)
				break;
			if(used)
			{
				invertPageTable[clockAt].process.setUsed(invertPageTable[clockAt].vpn,false);
		    	for(int i = 0;i<tlbSize;i++)
		    	{
		    	   	e = processor.readTLBEntry(i);
		    	   	if (e.valid && (e.vpn == invertPageTable[clockAt].vpn) 
		    	   		&& (e.ppn == invertPageTable[clockAt].ppn))
		    	   	{
		    	   		e.used = false;
		    	   		processor.writeTLBEntry(i, e);
		    	   		break;
		    	   	}
		    	}
			}
			clockAt = (clockAt + 1) % numPages;
			used = invertPageTable[clockAt].process.getUsed(invertPageTable[clockAt].vpn);
			pinned = invertPageTable[clockAt].pinned;
		}
		InvertTranslationEntry entry 
			= new InvertTranslationEntry(invertPageTable[clockAt]);
		clockAt = (clockAt + 1) % numPages;
//		lock.release();
    	return entry;
    }
    // dummy variables to make javac smarter
    private static VMProcess dummy1 = null;

    private static final char dbgVM = 'v';
    /*jill add*/
    private static int clockAt = 0;
    public static int numPinned = 0;
    public static InvertTranslationEntry[] invertPageTable;
//    public static Lock lockIpt = null;
    public static OpenFile swapFile;
    public static LinkedList<Integer> freeSwapPages;
//    public static Semaphore semaphoreSwapFile = null;
    public static Condition cond;
    /*jill end*/
}